package com.jingdianjichi.wx.controller;


import com.jingdianjichi.wx.ai.AnswerCache;
import com.jingdianjichi.wx.ai.QuestionKeyUtil;
import com.jingdianjichi.wx.ai.WechatKfClient;
import com.jingdianjichi.wx.entity.PendingQuestion;
import com.jingdianjichi.wx.handler.WxChatMsgFactory;
import com.jingdianjichi.wx.handler.WxChatMsgHandler;
import com.jingdianjichi.wx.mapper.PendingQuestionMapper;
import com.jingdianjichi.wx.service.impl.ChatService;
import com.jingdianjichi.wx.utils.MessageUtil;
import com.jingdianjichi.wx.utils.SHA1;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.net.SocketTimeoutException;
import java.util.Date;
import java.util.concurrent.*;

import javax.annotation.Resource;
import java.time.Instant;
import java.util.Map;

@RestController
@Slf4j
@RequiredArgsConstructor
public class CallBackController {
    private static String token="dingshao666";
    private final WxChatMsgFactory wxChatMsgFactory;

    private final ChatService chatService;
    private final AnswerCache answerCache;
    private final PendingQuestionMapper pendingMapper;

    private static final Executor EXEC = Executors.newCachedThreadPool();
    private static final long WAIT_MS = 3500;
    private static final long PENDING_TTL_MS = TimeUnit.MINUTES.toMillis(5);

    @RequestMapping("/test")
    public String test(){
        return "窝子";
    }

    @GetMapping("/callback")
    public String callback(@RequestParam("signature")String signature,
                           @RequestParam("timestamp")String timestamp,
                           @RequestParam("nonce")String nonce,
                           @RequestParam("echostr")String echostr){
        log.info("打印get请求参数：signature:{},timestamp:{},nonce:{},echostr:{}",signature,timestamp,nonce,echostr);
        String shaStr = SHA1.getSHA1(token, timestamp, nonce, "");
        if(signature.equals(shaStr)){
            return echostr;
        }
        return "unknown";
    }

    @PostMapping(value = "/callback",produces = "text/xml;charset=UTF-8")
    public String callback(@RequestBody String requestBody,
            @RequestParam("signature")String signature,
                           @RequestParam("timestamp")String timestamp,
                           @RequestParam("nonce")String nonce,
                           @RequestParam(value = "msg_signature",required = false)String msgSignature){
        log.info("打印post请求参数：signature:{},timestamp:{},nonce:{},msgSignature:{}",signature,timestamp,nonce,msgSignature);
        //用map接收参数
        Map<String,String> msgMap= MessageUtil.parseXml(requestBody);
        String toUserName= msgMap.get("ToUserName");
        String fromUserName= msgMap.get("FromUserName");
        String content= msgMap.get("Content");
        log.info("接收到的消息：{}",content);
        String msgId        = msgMap.get("MsgId");
        String msgType=msgMap.get("MsgType");
        String event=msgMap.get("Event")==null?"":msgMap.get("Event");
        log.info("msgType:{},event:{}",msgType,event);
        StringBuilder sb=new StringBuilder();
        sb.append(msgType);
        //用于处理关注后回复第一条消息
        if(!StringUtils.isEmpty(event)){
            sb.append(".");
            sb.append(event);
            String msgTypeKey=sb.toString();

            WxChatMsgHandler wxChatMsgHandler= wxChatMsgFactory.getHandlerByMsgType(msgTypeKey);
            String replyContent= wxChatMsgHandler.dealMsg(msgMap);

            log.info("replyContent:{}",replyContent);

            return xml(fromUserName, toUserName, replyContent);
        }else if("验证码".equals(content)){
            String msgTypeKey=sb.toString();
            WxChatMsgHandler wxChatMsgHandler= wxChatMsgFactory.getHandlerByMsgType(msgTypeKey);
            String replyContent= wxChatMsgHandler.dealMsg(msgMap);
            log.info("replyContent:{}",replyContent);
            return xml(fromUserName, toUserName, replyContent);
        }
        String qKey = QuestionKeyUtil.keyFor(content);

        // 1) 先查缓存
        String cached = answerCache.getByQuestionKey(qKey);
        if (cached == null) {
            PendingQuestion answered = pendingMapper.findLatestAnsweredByKey(qKey);
            if (answered != null && answered.getAnswerText() != null) {
                cached = answered.getAnswerText();
                answerCache.put(qKey, cached, 24*3600);
            }
        }
        if (cached != null) {
            return xml(fromUserName, toUserName, cached);
        }

        // 2) 未命中：限时 3.5s 等 AI，失败或超时→入库 PENDING 并立刻回占位
        long start = System.nanoTime();
        try {
            CompletableFuture<String> fut = CompletableFuture.supplyAsync(
                    () -> chatService.reply(content), EXEC);

            String aiReply = fut.get(WAIT_MS, TimeUnit.MILLISECONDS);
            if (aiReply != null && !aiReply.isEmpty()) {
                answerCache.put(qKey, aiReply, 24*3600);
            }
            return xml(fromUserName, toUserName, aiReply);

        } catch (TimeoutException te) {
            // 明确超时：入库 PENDING
            insertPendingSafe(fromUserName, msgId, content, qKey);
            return xml(fromUserName, toUserName, "我这边正在整理答案～5分钟内你可再问一次相同问题，我会直接给你结果哦");

        } catch (ExecutionException ee) {
            // AI 调用内部异常：如果是网络/超时类，也按“慢”处理 → 入库
            Throwable cause = ee.getCause();
            long elapsedMs = (System.nanoTime() - start) / 1_000_000;
            if (cause instanceof SocketTimeoutException || elapsedMs >= WAIT_MS - 50) {
                insertPendingSafe(fromUserName, msgId, content, qKey);
                return xml(fromUserName, toUserName, "我这边正在整理答案～5分钟内你可再问一次相同问题，我会直接给你结果哦");
            }
            // 其他异常 → 直接给兜底文案
            return xml(fromUserName, toUserName, "服务繁忙，请稍后再试~");

        } catch (Exception e) {
            // 任何其他异常：若整体耗时已接近 3.5s，也按“慢”处理
            long elapsedMs = (System.nanoTime() - start) / 1_000_000;
            if (elapsedMs >= WAIT_MS - 50) {
                insertPendingSafe(fromUserName, msgId, content, qKey);
                return xml(fromUserName, toUserName, "我这边正在整理答案～5分钟内你可再问一次相同问题，我会直接给你结果哦");
            }
            return xml(fromUserName, toUserName, "服务繁忙，请稍后再试~");
        }
    }

    private void insertPendingSafe(String openId, String msgId, String question, String qKey) {
        try {
            java.util.Date expireAt = new java.util.Date(System.currentTimeMillis() + PENDING_TTL_MS);
            pendingMapper.insertPending(openId, msgId, question, qKey, expireAt);
        } catch (Exception ex) {
            log.warn("插入PENDING失败 msgId={}, err={}", msgId, ex.toString());
        }
    }
    private String xml(String to, String from, String text){
        long now = System.currentTimeMillis() / 1000;
        return "<xml>\n" +
                "  <ToUserName><![CDATA[" + to + "]]></ToUserName>\n" +
                "  <FromUserName><![CDATA[" + from + "]]></FromUserName>\n" +
                "  <CreateTime>" + now + "</CreateTime>\n" +
                "  <MsgType><![CDATA[text]]></MsgType>\n" +
                "  <Content><![CDATA[" + (text==null?"":text) + "]]></Content>\n" +
                "</xml>";
    }

}
